Repository navigation
feat: RFC-0045 step 7b, enable reference and repeater fields - #223
Merged
Merged
Conversation
…ference and repeater fields - The gate accepts both types wherever the renderer serving the site reads them: the admin Worker asks the site Worker for `fields: 2` (siteSupport, beside the gates check); openLocalStore and the playground pass FIELDS_SUPPORTED; the admin push route passes the store's capability; cloudhal pull, import (dry runs too) and --revert pass the local store's. Without a capability the types stay refused, bundles included. - One-time per-site entry_refs rebuild (rebuildEntryRefs): rows from the import rebuild's own code, each node anchored on its read hash, the collections anchor on every statement, resumable by cursor in the new entry_refs_state table (migration 0034), orphans removed per site. Wired as the admin Worker's refs.rebuild system job and in openLocalStore. - Tests: the job on SQLite, D1 and DO (R17, cursor, orphans, definitions change, rows equal writer and import rebuild); bundle gate with and without the capability for import, push, pull and restore; a capable site's held site body cannot be re-pointed by another site's snapshot; API push and workerd e2e against old and new site Workers. - docs/guides/fields.md documents both types; RFC "Amended in 7b"; ADR-0046 and comments updated; changeset (patch). Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Madraka <[email protected]>
- The capability follows the site Worker serving each site: siteSupport asks the binding a site is routed to (siteWorkerFor: CLOUDHAL_SITE_ROUTES, else SITE) and remembers fields and gates per binding, so a site on an older attached Worker keeps refusing. A failed probe is not remembered. - rebuildEntryRefs leaves a site alone while a text <-> reference conversion runs there (operation_running, cursor unmoved): its rows decide what phase B accepted, which a rebuild from bodies cannot know. - The job's anchor is the site node's hash, not the $.collections text, so no statement grows with the site's settings and one entry always fits a Free run. - docs/guides/fields.md: a move answers reference_target_moved; the capability is per site Worker. RFC "Amended in 7b" and changeset updated. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Madraka <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
RFC-0045 rollout step 7b: enables
referenceandrepeaterfields in production, wherever the renderer serving a site reads them. See RFC §9 step 7 and the new "Amended in 7b" section.Production call sites and the capability decision
cloudflare/src/admin-worker.ts)fields: 2from the site Worker serving that site viasiteSupport(features.ts): it probes the binding the site is routed to (siteWorkerFor:CLOUDHAL_SITE_ROUTES, elseSITE) and remembers a yes per binding; time gates follow the same rule (round 1)PUT /api/admin/sites/:id/bundle(api/routes/admin/sync.ts)fieldTypes: store.fieldTypesopenLocalStore(core-ts/src/local.ts):cloudhal dev,studio,mcp, self-host, Docker, every local CLI commandFIELDS_SUPPORTEDGATES_ENFORCED)cloudhal pull(cli/commands/sync.ts) →importSite(local)local.store.fieldTypescloudhal importapply (import-apply.ts) and--revertapplylocal.store.fieldTypescloudhal importdry run (import.ts): local store / in-memory store for a new projectlocal.store.fieldTypes/FIELDS_SUPPORTEDcloudhal import --revertplan (import-revert.ts)local.store.fieldTypescloudflare/src/playground-site.ts)FIELDS_SUPPORTEDoptions.render)cloudflare/src/scheduler.ts: release runs, approval checks)astro-renderer/src/runtime-workerd.ts)core-ts/src/references/release-purge.ts)scripts/demo-archive.tsopenLocalStoreMCP (
update_collection,change_field_typeinputs) and Studio (/api/instancefeatures.referenceFields) already key onstore.fieldTypes !== undefined(steps 5 and 6b), so they now list the types on the admin Worker, Node and the playground. The store still decides per site.One-time
entry_refsrebuild job (§2.4)rebuildEntryRefs(core-ts/src/references/rebuild-job.ts, exported from@cloudhal/core-ts). Rows come fromentryRefItemsOf, split out of the import rebuild (references/rebuild.ts), so the job usesrebuildRowswithfromKey,oldShapePairsand the text↔reference rules, and the holder'ssource_status.DELETEand chunkedINSERTs (each below 90 KB). Each row is joined tonodeson(id, read hash)throughjson_eachitems. This anchors each node exactly as one statement pair per node would, but in a fixed number of statements per chunk, which fits the free tick (40 statements). It never deletes site-wide.hash, read with the chunk (round 1; the collections text could reach 256 KB per statement). A definitions change, or any other site edit, fails the whole chunk, which is then re-read.operation_running, cursor unmoved; round 1), because the rows decide what phase B accepted.entry_refs_state (site_id, cursor, done, updated_at)(migration0034_entry_refs_state, added toSITE_TABLES).nodes.key. The advance is an upsert guarded on the cursor as read.source_id NOT IN (SELECT id FROM nodes WHERE site_id = ?)) and marks the site done.lists.backfill:refs.rebuildsystem job (cloudflare/src/entry-refs-job.ts): every 5 min at :02:30, 12 statements on Free and 150 on Paid, two reads once done;openLocalStoreat start-up.lists.backfillhas no CLI command or route, so this job has neither, and no API route was added.comments.test.tsnow starts from the steady state.Gate tests and the step-1 watch note
testing-gates-fields.tsruns fromgatesSuiteon SQLite and D1. It covers import, pull, push (published: target) and restore (restoreBundle).heldSiteBodies(step 1/2) already reads the held site bodies that new snapshots name. A new test shows that a site without the capability cannot point a new snapshot at a capable site's held body. It fails whenheldSiteBodiesis disabled.admin.test.tspush is accepted with the site's capability (200) and refused with another site's (409).cli/test/cloudflare-gates.test.ts): a push with a reference field is accepted against the real site Worker (fields: 2) and refused with409 fields_unsupportedagainst the old one.testing-entry-refs-job.tsruns on SQLite, Miniflare D1 and DO SQLite. It covers:source_statusincluded).cloudflare/test/entry-refs-job.test.tschecks, on D1, the declared cost per run, the logging, and the steady state.Other changes
CopySiteOptions.fieldTypesacceptsundefined, so callers can passlocal.store.fieldTypes.cli/test/fields-cli.test.ts: the local store now accepts a retype to a repeater (dry run).docs/guides/fields.mdcovers both types:sub, conversions and "Clear and finish";fieldsStrict/clearFields, translations andwholeLists;EntryFields.astrorendering;FIELD_TYPESstays the nine types; Studio and MCP already addREFERENCE_FIELD_TYPESbehind the capability.node scripts/mcp-tools-doc.tsnow renders fromdefineTools({ referenceTypes: true }). The README table is unchanged because it shows first sentences only.rfc45-step7b-enable.md: patch (0.x rule). It says the types are now enabled.Review round 1 (42a50ef)
SITEreportsfields: 2andSITE_1does not: the site routed toSITE_1is refused and the main site is accepted. Gates use the same per-binding rule.collectionsover 90 KB (about 130 KB) and an entry with 20 references, the job finishes within the Free budget (≤ 12 statements a run, every statement < 90 KB).reference_target_moved. I checked its other codes againstapi/src/errors.ts.Tests run after round 1, one file at a time:
cloudflare-gates(9/9) andcloudflare-multisite(6/6) on workerd;Local checks (first round)
pnpm check(run 2): every step before the tests passed (biome, size check, font fallbacks, MCP tools doc, demo archive, typecheck). In the test stage at--concurrency=3, two Studio UI files failed under machine load; both pass alone. Turbo then stopped before core-ts, api, cli and cloudflare.turbo run test --concurrency=1 --continue: 17 of 19 packages passed, Studio included (85 files). The other two:store.test.ts: only the D1 variants failed, withEADDRNOTAVAIL. One D1 pass fills this machine's port range (more than 8,000 sockets inTIME_WAIT), a known local issue.studio-site-build.test.ts: an 11 s build timeout under load. It passes alone twice (7/7).core-ts
store.test.tsrun again in slices, waiting for the ports to drain between slices:SiteGraphStore, 7 time-gate tests, 48 in the other suites;SiteGraphStore, plus 7.All pass, with no
EADDRNOTAVAIL.cli/test/cloudflare-gates.test.tson workerd: 9/9.Not merged; waiting for the PM review.
🤖 Generated with Claude Code